home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / castools.zip / PBUTILS.C < prev    next >
Text File  |  1990-02-13  |  12KB  |  426 lines

  1.  
  2.  
  3. /*
  4.   PBUTILS.C  Phonebook Library internal utilities
  5. */
  6.  
  7.  
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include <ctype.h>
  11. #include <malloc.h>
  12. #include <phonebk.h>
  13.  
  14. /*
  15.   PBLIB Internal Utility:  EntryOkToAdd()
  16.  
  17.   Performs some validity checks on a phonebook entry for adding.
  18.  
  19.   INPUT:  A phonebook, and the entry.
  20.  
  21.   OUTPUT:  1 or 0.
  22. */
  23.  
  24. char * pascal EntryOkToAdd(PB *pb, PBE *entry)
  25. {
  26.   int i, j, k;
  27.   char *fields;
  28.   int *members;
  29.   PBEFIXED fixed_part, *result;
  30.   char *return_buffer;
  31.  
  32.   Pberrno = 0;                               /* Initially, always reset */
  33.  
  34.   /* Check that the name field is neither empty, only blank(s), or too long */
  35.   for (i=0; entry->name[i] && (i < NAMELENGTH); i++) {
  36.     if (entry->name[i] != ' ') {
  37.       break;
  38.     }
  39.   }
  40.   if ((!entry->name[i]) || (i == NAMELENGTH)) {
  41.     return(NULL);
  42.   }
  43.  
  44.   /* Check all the entries for a possible name match with the new entry */
  45.   for (i=0; i<MAXENTRIES; i++) {
  46.     if (GetFixedPart(pb, &fixed_part, i)) {
  47.       if (!strnicmp(entry->name, fixed_part.name, NAMELENGTH)) {
  48.         Pberrno = DUPLICATEENTRY;
  49.         return(NULL);
  50.       }
  51.     }
  52.   }
  53.  
  54.   /* Checks specific to person entries */
  55.   if (entry->type == PERSONENTRY) {
  56.  
  57.     /* New entries can have members only if they are group entries */
  58.     if (entry->members) {
  59.       return(NULL);
  60.     }
  61.  
  62.     /* check that the phone number is present and valid. */
  63.     /* (If the first character of phone number is 'm' or 'M', rest is ok) */
  64.     if (tolower(entry->PhoneNumber[0]) != 'm') {
  65.       for (i=0; entry->PhoneNumber[i] && (i < PHONENUMLENGTH); i++) {
  66.         if ((entry->PhoneNumber[i] >= '0') &&
  67.             (entry->PhoneNumber[i] <= '9')) {
  68.           break;
  69.         }
  70.       }
  71.       if ((!entry->PhoneNumber[i]) || (i == PHONENUMLENGTH)) {
  72.         return(NULL);
  73.       }
  74.     }
  75.  
  76.     /* check that entry has pb->fields ASCIIZ fields  */
  77.     for (i=0, k=0; i<pb->header.fields; i++, k++) {
  78.       if (!(entry->fields[i])) {
  79.         break;
  80.       }
  81.       for (j=0; j<MAXFIELDSIZE; j++, k++) {
  82.         if (!entry->fields[i][j]) {
  83.           break;
  84.         }
  85.       }
  86.       if (j == MAXFIELDSIZE) {
  87.         return(NULL);
  88.       }
  89.     }
  90.     if (i<pb->header.fields) {
  91.       return(NULL);
  92.     }
  93.  
  94.     /* finally, length is not checked, but set (assume no members list.) */
  95.     entry->length = sizeof(PBEFIXED) + k;
  96.   }
  97.  
  98.   /* Now, the checks for group entries */
  99.   else if (entry->type == GROUPENTRY) {
  100.  
  101.     /* Check that the members are in the phonebook and are person entries */
  102.     if (entry->members) {
  103.       for (i=0; i<entry->members; i++) {
  104.         if (!(result = GetFixedPart(pb, &fixed_part, entry->MemberList[i]))) {
  105.           return(NULL);
  106.         }
  107.         if (fixed_part.type != PERSONENTRY) {
  108.           return(NULL);
  109.         }
  110.       }
  111.  
  112.       /* And, because PbAddEntry will use PbAddToGroup, null out membership */
  113.       entry->members = 0;
  114.     }
  115.  
  116.     /* check that the phone number field is all NULL */
  117.     for (i=0; i<PHONENUMLENGTH; i++) {
  118.       if (entry->PhoneNumber[i]) {
  119.         return(NULL);
  120.       }
  121.     }
  122.  
  123.     /* The entry length is not checked, but set */
  124.     entry->length = sizeof(PBEFIXED);  /* space for members list NOT included */
  125.   }
  126.   else {
  127.     return(NULL);          /* invalid type */
  128.   }
  129.  
  130.   /* If all that passed, we must be ok, fill in the return buffer! */
  131.   if (!(return_buffer = (char *)malloc(entry->length))) {
  132.     Pberrno = OUTOFMEM;
  133.     return(NULL);
  134.   }
  135.   memcpy(return_buffer, entry, sizeof(PBEFIXED));
  136.   if (entry->type == PERSONENTRY) {
  137.     fields = return_buffer + sizeof(PBEFIXED);
  138.     for (i=0, j=0; i<pb->header.fields; i++, j++) {
  139.       for (k=0; fields[j] = entry->fields[i][k]; j++, k++) {
  140.         ;
  141.       }
  142.     }
  143.   }
  144.   return(return_buffer);
  145. }
  146.  
  147. /*
  148.   PBLIB Internal Utility:  GetFixedPart()
  149.  
  150.   Gets the fixed part of a phonebook entry, given the record id.
  151.  
  152.   INPUT:  The phonebook, a buffer to read the entry into, and the record id.
  153.  
  154.   OUTPUT:  The entry information, if the offset was non-NULL at that record id.
  155.            If an error occurs, Pberrno is set, and function returns NULL.
  156.            If no error occurs, function returns the same pointer it was given.
  157.            If the offset at that record id was NULL, (no record there), function
  158.               returns NULL, but Pberrno is NOT set.
  159.  
  160. */
  161.  
  162. PBEFIXED * pascal GetFixedPart(PB *pb, PBEFIXED *retinfo, WORD rid)
  163. {
  164.   size_t buffed_rids;
  165.   long offset;
  166.   int red;                  /* for return value of fread() */
  167.  
  168.   Pberrno = 0;            /* Initially, always reset */
  169.  
  170.   /* First, get the offset */
  171.   offset = OffsetOfEntry(pb, rid);
  172.  
  173.   /* Once the offset is gotten, go there, and read the entry */
  174.   if (!offset) {
  175.     return(NULL);
  176.   }
  177.   if (fseek(pb->fp, offset, SEEK_SET)) {
  178.     Pberrno = FSEEKERROR;
  179.     return(NULL);
  180.   }
  181.   red = fread(retinfo, 1, sizeof(PBEFIXED), pb->fp);
  182.   if (red != sizeof(PBEFIXED)) {
  183.     Pberrno = CANTREAD;
  184.     return(NULL);
  185.   }
  186.   return(retinfo);
  187. }
  188.  
  189. /*
  190.   PBLIB Internal Utility:  EntryOkToChange()
  191.  
  192.   Performs validity checks on a proposed change to an entry.
  193.  
  194.   INPUT:  A phonebook, the old entry, and a proposed changed entry.
  195.  
  196.   OUTPUT:  Returns pointer to buffer containing the changed entry, in the
  197.            format used in the phonebook file, not as a PBE.
  198. */
  199.  
  200. char *pascal EntryOkToChange(PB *pb, PBE *old_entry, PBE *new_entry)
  201. {
  202.   int i, j, k;
  203.   PBEFIXED fixed_part;
  204.   char *return_buffer;
  205.   char *fields;
  206.   int *members;
  207.  
  208.   Pberrno = 0;                               /* Initially, always reset */
  209.  
  210.   /* Change of entry type not allowed */
  211.   if (old_entry->type != new_entry->type) {
  212.     return(NULL);
  213.   }
  214.  
  215.   /* If name is changed, ...*/
  216.   if (strnicmp(old_entry->name, new_entry->name, NAMELENGTH)) {
  217.  
  218.     /* ... it may not be empty, nor all blank(s), nor too long */
  219.     for (i=0; new_entry->name[i] && (i < NAMELENGTH); i++) {
  220.       if (new_entry->name[i] != ' ') {
  221.         break;
  222.       }
  223.     }
  224.     if ((!new_entry->name[i]) || (i == NAMELENGTH)) {
  225.       return(NULL);
  226.     }
  227.  
  228.     /* ... nor may it match any other entry's name */
  229.     for (i=0; i<MAXENTRIES; i++) {
  230.       if (GetFixedPart(pb, &fixed_part, i)) {
  231.         if (!(strnicmp(new_entry->name, fixed_part.name, NAMELENGTH))) {
  232.           Pberrno = DUPLICATEENTRY;
  233.           return(NULL);
  234.         }
  235.       }
  236.     }
  237.   }
  238.  
  239.   /* No changes to membership count or members list allowed */
  240.   if (old_entry->members != new_entry->members) {
  241.     return(NULL);
  242.   }
  243.   for (i=0; i<old_entry->members; i++) {
  244.     if (old_entry->MemberList[i] != new_entry->MemberList[i]) {
  245.       return(NULL);
  246.     }
  247.   }
  248.  
  249.   /* Checks specific to group entries */
  250.   if (old_entry->type == GROUPENTRY) {
  251.  
  252.     /* Actually, no fields but name may change, but rest are covered elsewhere*/
  253.     if ((old_entry->length != new_entry->length) ||
  254.         (old_entry->HardwareType != new_entry->HardwareType)) {
  255.       return(NULL);
  256.     }
  257.  
  258.     /* The 'PhoneNumber' field for groups must remain all NULL's */
  259.     for (i=0; i<PHONENUMLENGTH; i++) {
  260.       if (new_entry->PhoneNumber[i]) {
  261.         return(NULL);
  262.       }
  263.     }
  264.   }
  265.  
  266.   /* Checks specific to person entries */
  267.   else {
  268.  
  269.     /* check that the phone number is present and valid. */
  270.     /* (If the first character of phone number is 'm' or 'M', rest is ok) */
  271.     if (tolower(new_entry->PhoneNumber[0]) != 'm') {
  272.       for (i=0; new_entry->PhoneNumber[i] && (i < PHONENUMLENGTH); i++) {
  273.         if ((new_entry->PhoneNumber[i] >= '0') ||
  274.             (new_entry->PhoneNumber[i] <= '9')) {
  275.           break;
  276.         }
  277.       }
  278.       if ((!new_entry->PhoneNumber[i]) || (i == PHONENUMLENGTH)) {
  279.         return(NULL);
  280.       }
  281.     }
  282.  
  283.     /* check that entry has pb->fields ASCIIZ fields  */
  284.     for (i=0, k=0; i<pb->header.fields; i++, k++) {
  285.       if (!(new_entry->fields[i])) {
  286.         break;
  287.       }
  288.       for (j=0; j<MAXFIELDSIZE; j++, k++) {
  289.         if (!new_entry->fields[i][j]) {
  290.           break;
  291.         }
  292.       }
  293.       if (j == MAXFIELDSIZE) {
  294.         return(NULL);
  295.       }
  296.     }
  297.     if (i<pb->header.fields) {
  298.       return(NULL);
  299.     }
  300.  
  301.     /* Set the length based on sizeof fixed part, plus fields and members */
  302.     new_entry->length = sizeof(PBEFIXED) + k +
  303.                         new_entry->members * sizeof(int);
  304.  
  305.     /* Only person entries may change size, so check here if the new
  306.        entry is larger. The size of the old entry must not put the
  307.        phonebook's 'unused bytes' over the limit */
  308.     if (new_entry->length > old_entry->length) {
  309.       if (pb->header.FreeBytes + old_entry->length
  310.           > MAXUNUSEDBYTES) {
  311.         return(NULL);
  312.       }
  313.     }
  314.   }
  315.  
  316.   /* If all that passed, we must be ok, fill in the return buffer! */
  317.   if (!(return_buffer = (char *)malloc(new_entry->length))) {
  318.     Pberrno = OUTOFMEM;
  319.     return(NULL);
  320.   }
  321.   memcpy(return_buffer, new_entry, sizeof(PBEFIXED));
  322.   fields = return_buffer + sizeof(PBEFIXED);
  323.   if (new_entry->type == PERSONENTRY) {
  324.     for (i=0, j=0; i<pb->header.fields; i++, j++) {
  325.       for (k=0; fields[j] = new_entry->fields[i][k]; j++, k++) {
  326.        ;
  327.       }
  328.     }
  329.   }
  330.   else j = 0;
  331.   members = (int *)&fields[j];
  332.   for (i=0; i<new_entry->members; i++) {
  333.     members[i] = new_entry->MemberList[i];
  334.   }
  335.   return(return_buffer);
  336. }
  337.  
  338. /*
  339.   PBLIB Internal Utility:  OffsetOfEntry()
  340.  
  341.   Finds the offset of an entry, given its record id.  Uses the Pb Offset
  342.   buffer if it is present.
  343.  
  344.   INPUT:  A phonebook and the record id of the entry to find the offset of.
  345.  
  346.   OUTPUT:  Returns the LONGWORD offset.
  347. */
  348.  
  349. LONGWORD pascal OffsetOfEntry(PB *pb, WORD rid)
  350. {
  351.   int buffed_rids;
  352.   LONGWORD offset;
  353.   int red;                      /* for returns from fread() */
  354.  
  355.   Pberrno = 0;                               /* Initially, always reset */
  356.  
  357.   if (pb->OBuffer) {                                /* IF buffering is ON */
  358.     buffed_rids = pb->OBufferSize/sizeof(LONGWORD);
  359.  
  360.     /* First, if the requested rid is NOT in the buffer, fetch needed section */
  361.     if ((rid < pb->FirstOBufferRID) ||
  362.         (rid > pb->FirstOBufferRID + buffed_rids - 1)) {
  363.       pb->FirstOBufferRID = 0;
  364.       while (rid > pb->FirstOBufferRID + buffed_rids - 1) {
  365.         pb->FirstOBufferRID += buffed_rids;
  366.       }
  367.       if (fseek(pb->fp,
  368.                 160L + pb->FirstOBufferRID * sizeof(LONGWORD),
  369.                 SEEK_SET)) {
  370.         Pberrno = FSEEKERROR;
  371.         return(NULL);
  372.       }
  373.       red = fread(pb->OBuffer, sizeof(LONGWORD), buffed_rids, pb->fp);
  374.       if (red != buffed_rids) {
  375.         Pberrno = CANTREAD;
  376.         return(NULL);
  377.       }
  378.     }
  379.     return(pb->OBuffer[rid - pb->FirstOBufferRID]);
  380.   }
  381.  
  382.    /* If no buffering, just seek to the needed spot, and read the offset */
  383.   else {
  384.     if (fseek(pb->fp, 160L + rid * sizeof(LONGWORD), SEEK_SET)) {
  385.       Pberrno = FSEEKERROR;
  386.       return(NULL);
  387.     }
  388.     red = fread(&offset, sizeof(LONGWORD), 1, pb->fp);
  389.     if (red != 1) {
  390.       Pberrno = CANTREAD;
  391.       return(NULL);
  392.     }
  393.     return(offset);
  394.   }
  395. }
  396.  
  397. /*
  398.   PBLIB Internal Utility:  HardwareTypeOf()
  399.  
  400.   Determines whether the HardwareType of an entry at a given record id is HASCCC
  401.   or FAXONLY.
  402.  
  403.   INPUT:  A phonebook and the record id of the entry to find the HardwareType of,
  404.           and a pointer to a LONGWORD for returning the offset of the HardwareType
  405.           field.
  406.  
  407.   OUTPUT:  Returns HASCCC or FAXONLY, and sets the offset to the entry's
  408.            HardwareType field.
  409. */
  410.  
  411. BYTE pascal HardwareTypeOf(PB *pb, WORD RecordID, LONGWORD *offset)
  412. {
  413.   BYTE HardwareType = 0;            /* Invalid HardwareType is default if error occurs */
  414.  
  415.   Pberrno = 0;                               /* Initially, always reset */
  416.  
  417.   *offset = OffsetOfEntry(pb, RecordID) + 7L;
  418.   if (fseek(pb->fp, *offset, SEEK_SET)) {
  419.     Pberrno = FSEEKERROR;
  420.   }
  421.   if (fread(&HardwareType, 1, sizeof(BYTE), pb->fp) != 1) {
  422.     Pberrno = CANTREAD;
  423.   }
  424.   return(HardwareType);
  425. }
  426.